use std::env;
use std::fs::File;
use std::path::PathBuf;

use clap::{Arg, ArgAction, Parser, ValueHint};
use env_logger::Env;
use fatfs::{FileSystem, FsOptions};
use fuse3::{Fuse, MountOptions};
use log::{debug, info};
use anyhow::{Context, Result};

mod error;
mod fatfs_fuse;

#[derive(Debug, Parser)]
#[command(author = "FUSE FATFS Project", version = "0.1.0", about = "Mounts a FAT filesystem image using FUSE3", long_about = None)]
struct Args {
    /// FAT filesystem image file path
    #[arg(required = true, value_hint = ValueHint::FilePath)]
    image: String,

    /// Directory to mount the filesystem
    #[arg(required = true, value_hint = ValueHint::DirPath)]
    mount_point: String,

    /// Mount read-only
    #[arg(short = 'r', long, action = ArgAction::SetTrue)]
    readonly: bool,

    /// Enable debug output
    #[arg(short = 'd', long, action = ArgAction::SetTrue)]
    debug: bool,

    /// Run in foreground instead of daemonizing
    #[arg(short = 'f', long, action = ArgAction::SetTrue)]
    foreground: bool,

    /// Allow other users to access the filesystem
    #[arg(long, action = ArgAction::SetTrue)]
    allow_other: bool,

    /// Set the owner user ID of all files/directories
    #[arg(long, value_name = "UID")]
    uid: Option<String>,

    /// Set the owner group ID of all files/directories
    #[arg(long, value_name = "GID")]
    gid: Option<String>,

    /// Set the umask for files/directories
    #[arg(long, value_name = "UMASK")]
    umask: Option<String>,
}

fn main() -> Result<()> {
    let args = Args::parse();

    // Initialize logger with proper log level
    let log_level = if args.debug {
        "debug"
    } else {
        "info"
    };
    env_logger::Builder::from_env(Env::default().default_filter_or(log_level)).init();

    // Set debug mode environment variable if requested
    if args.debug {
        env::set_var("RUST_LOG", "debug");
        debug!("Debug mode enabled");
    }

    // Validate mount point exists and is a directory
    let mount_point_path = PathBuf::from(&args.mount_point);
    if !mount_point_path.exists() || !mount_point_path.is_dir() {
        return Err(anyhow::anyhow!("Mount point '{}' does not exist or is not a directory", args.mount_point));
    }

    info!("Mounting FAT image '{}' to '{}'", args.image, args.mount_point);

    // Open the FAT image file
    let image_file = File::open(&args.image)
        .with_context(|| format!("Failed to open image file '{}'", args.image))?;

    // Mount the FAT filesystem with read-only option if specified
    let fs_options = FsOptions::new().read_only(args.readonly);
    let fs = FileSystem::new(image_file, fs_options)
        .with_context(|| "Failed to mount FAT filesystem")?;

    // Create the FUSE filesystem
    let fs = Arc::new(fs);
    let filesystem = fatfs_fuse::FatFsFuse::new(fs);

    // Prepare mount options
    let mut mount_options = MountOptions::new();
    mount_options.read_only(args.readonly);
    
    if args.allow_other {
        mount_options.allow_other(true);
    }
    
    if args.foreground {
        mount_options.foreground(true);
    }
    
    // Configure UID, GID, and umask if provided
    if let Some(uid) = &args.uid {
        mount_options.uid(uid.parse()?);
    }
    
    if let Some(gid) = &args.gid {
        mount_options.gid(gid.parse()?);
    }
    
    if let Some(umask) = &args.umask {
        mount_options.umask(umask.parse()?);
    }

    info!("Mounting with options: readonly={}, debug={}, allow_other={}", 
          args.readonly, args.debug, args.allow_other);

    // Mount the filesystem
    let mount_point = PathBuf::from(&args.mount_point);
    Fuse::new(filesystem, &mount_point, &mount_options)?;

    info!("Filesystem mounted successfully");

    // Wait for signals or forever
    let (_tx, rx) = std::sync::mpsc::channel();
    rx.recv()?;

    Ok(())
}